﻿using System;
using GeoAPI.Geometries;
using NetTopologySuite.Utilities;

namespace NetTopologySuite.LinearReferencing
{
	public class ExtractLineByLocation
	{
		public static IGeometry Extract(IGeometry line, LinearLocation start, LinearLocation end)
		{
			return new ExtractLineByLocation(line).Extract(start, end);
		}

		public ExtractLineByLocation(IGeometry line)
		{
			this._line = line;
		}

		public IGeometry Extract(LinearLocation start, LinearLocation end)
		{
			if (end.CompareTo(start) < 0)
			{
				return ExtractLineByLocation.Reverse(this.ComputeLinear(end, start));
			}
			return this.ComputeLinear(start, end);
		}

		private static IGeometry Reverse(IGeometry linear)
		{
			if (!(linear is ILineal))
			{
				Assert.ShouldNeverReachHere("non-linear geometry encountered");
				return null;
			}
			return linear.Reverse();
		}

		private IGeometry ComputeLinear(LinearLocation start, LinearLocation end)
		{
			LinearGeometryBuilder linearGeometryBuilder = new LinearGeometryBuilder(this._line.Factory);
			linearGeometryBuilder.FixInvalidLines = true;
			if (!start.IsVertex)
			{
				linearGeometryBuilder.Add(start.GetCoordinate(this._line));
			}
			LinearIterator linearIterator = new LinearIterator(this._line, start);
			while (linearIterator.HasNext() && end.CompareLocationValues(linearIterator.ComponentIndex, linearIterator.VertexIndex, 0.0) >= 0)
			{
				Coordinate segmentStart = linearIterator.SegmentStart;
				linearGeometryBuilder.Add(segmentStart);
				if (linearIterator.IsEndOfLine)
				{
					linearGeometryBuilder.EndLine();
				}
				linearIterator.Next();
			}
			if (!end.IsVertex)
			{
				linearGeometryBuilder.Add(end.GetCoordinate(this._line));
			}
			return linearGeometryBuilder.GetGeometry();
		}

		private readonly IGeometry _line;
	}
}
